#pragma once

#include "zrpc/base/log.h"
#include "zrpc/net/event_loop.h"
#include "zrpc/net/tcp_connection.h"
#include "zrpc/net/tcp_server.h"

#define REDIS_REQ_INLINE 1
#define REDIS_REQ_MULTIBULK 2

enum ReadStatus {
  kReadHalf = 0,
  kReadAll = 1,
  kReadError = 2,
  kReadClose = 3,
  kFullError = 4,
  kParseError = 5,
  kDealError = 6,
  kOk = 7,
};

typedef std::vector<std::string> RedisCmdArgs;
class RedisConn {
 public:
  typedef std::function<void(const RedisCmdArgs &,
                             const std::shared_ptr<zrpc::TcpConnection> &,
                             zrpc::Buffer *)>
      RedisCmdArgsCallbackFunc;

  RedisConn();
  ~RedisConn();

  void OnCommandCallback();
  void OnConnectionCallback(const std::shared_ptr<zrpc::TcpConnection> &conn);
  void OnMessageCallback(const std::shared_ptr<zrpc::TcpConnection> &conn,
                         zrpc::Buffer *buf);

  int FindNextSeparators();

  ReadStatus ProcessInputBuffer();
  ReadStatus ProcessMultibulkBuffer();
  ReadStatus ProcessInlineBuffer();

  int Strtoll(int pos, int32_t *value);

 private:
  zrpc::Buffer *rbuf_;
  zrpc::Buffer *wbuf_;
  std::vector<std::string> argv_;
  std::shared_ptr<zrpc::TcpConnection> conn_;
  std::unordered_map<std::string, std::string> db_set_commands_;

  int32_t req_type_;
  int32_t multibulk_len_;
  int32_t bulk_len_;

  int last_read_pos_;
  int next_parse_pos_;
  RedisCmdArgsCallbackFunc redis_cmd_args_callback_;
};

class RedisServer {
 public:
  RedisServer(const std::string &ip, int16_t port);
  ~RedisServer();

  void OnAcceptCallback(const int32_t fd);
  void OnConnectionCallback(const std::shared_ptr<zrpc::TcpConnection> &conn);
  void Run();

 private:
  zrpc::EventLoop event_loop_;
  zrpc::TcpServer tcp_server_;
  std::unordered_map<int32_t, std::shared_ptr<RedisConn>> redis_conns_;
};